/*
 * libmad - MPEG audio decoder library
 * Copyright (C) 2000-2004 Underbit Technologies, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id: minimad.c,v 1.4 2004/01/23 09:41:32 rob Exp $
 */



/* MAD
 * This is perhaps the simplest example use of the MAD high-level API.
 * Standard input is mapped into memory via mmap(), then the high-level API
 * is invoked with three callbacks: input, output, and error. The output
 * callback converts MAD's high-resolution PCM samples to 16 bits, then
 * writes them to standard output in little-endian, stereo-interleaved
 * format.
 */
/*MAD高级API的简单示例。标准输入通过mmap()函数被映射到内存中，高级API被三个
例程使用：input、output和error。output例程将MAD高分辨率PCM采样（24位）转换为16位，
然后以小端模式、立体声合并格式写入标准输出中。
*/

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>

#include <fcntl.h>
#include <sys/soundcard.h>		//提供驱动函数接口
#include "mad.h"

#undef putchar
int soundfd;				//声卡设备ID

static int decode(unsigned char const *, unsigned long);


// 真实写入声卡
int putchar(int c) {
   //putchar(c); 
   return write(soundfd, (char *)&c, 1);
}

//设置声卡工作参数
void set_dsp()
{
	int rate = 32000; 			// 采样频率 44.1KHz
	int format = AFMT_S16_LE; 		// 量化位数 16
	int channels = 2; 			// 声道数 2：立体声

	if((soundfd = open ( "/dev/dsp", O_WRONLY)) <0 )	//打开声卡设备/dev/dsp 
	{
		printf("cannot open /dev/dsp \n");
		exit(0);
	}
	ioctl(soundfd, SNDCTL_DSP_SPEED, &rate);			//设置采样频率
	ioctl(soundfd, SNDCTL_DSP_SETFMT, &format); 		//设置量化位数
	ioctl(soundfd, SNDCTL_DSP_CHANNELS, &channels); 	//设置声道数
}


/*minimad主程序
参数：传入mp3文件名
打开声卡，设置工作参数
*/
//工作方式：mmap()
int main(int argc, char *argv[])
{
	struct stat stat;
	void *fdm;

	printf("main: begin()\n");
	if (argc != 1)
		return 1;

	printf("main: set_dsp()\n");
	//打开并初始化声卡
	set_dsp();		

	//获取标准输入文件的状态：测试是否有效，及其文件总大小（即xx.mp3）
	if (fstat(STDIN_FILENO, &stat) == -1 ||	stat.st_size == 0)
		return 2;

	//映射内存：整个文件（0~stat.st_size），映射区只读、共享
	fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, STDIN_FILENO, 0);
	if (fdm == MAP_FAILED)
		return 3;

	//如果采用read()方式？

	//解码
	decode(fdm, stat.st_size);

	//释放内存映射
	if (munmap(fdm, stat.st_size) == -1)	
		return 4;

	//关闭声卡
	close(soundfd);

	return 0;
}


/*
 * This is a private message structure. A generic pointer to this structure
 * is passed to each of the callback functions. Put here any data you need
 * to access from within the callbacks.
 */
//一个私有的信息结构体。每个回调函数中，都有一个指向该结构体的指针。
//调函数中，利用该指针可以访问任何数据。
struct buffer {
  unsigned char const *start;
  unsigned long length;
};


/*
 * This is the input callback. The purpose of this callback is to (re)fill
 * the stream buffer which is to be decoded. In this example, an entire file
 * has been mapped into memory, so we just call mad_stream_buffer() with the
 * address and length of the mapping. When this callback is called a second
 * time, we are finished decoding.
 */
//输入回调函数：解码时，重新填充流缓冲区。
//例中，整个文件被映射到内存中，所以我们只需要调用mad_stream_buffer(映射地址，长度)。 
//当回调函数被调用一会，将完成解码。
static
enum mad_flow input(void *data,
		    struct mad_stream *stream)
{
  struct buffer *buffer = data;

  if (!buffer->length)
    return MAD_FLOW_STOP;

  //mad API：拷贝buffer，填充stream
  mad_stream_buffer(stream, buffer->start, buffer->length);

  buffer->length = 0;

  return MAD_FLOW_CONTINUE;
}


/*
 * The following utility routine performs simple rounding, clipping, and
 * scaling of MAD's high-resolution samples down to 16 bits. It does not
 * perform any dithering or noise shaping, which would be recommended to
 * obtain any exceptional audio quality. It is therefore not recommended to
 * use this routine if high-quality output is desired.
 */
//scale()例程执行取样的舍入和裁减，并将高分辨率的样点降低至16bit。
//它没有执行任何去抖和噪音修正，这些在想获得高质量的音频是必须的。所以不要期望本例程能有高质量的输出。
static inline
signed int scale(mad_fixed_t sample)
{
  /* round */						//有效位：14-29bit   30-32bit？
  sample += (1L << (MAD_F_FRACBITS - 16));	//舍掉后12位，第13位+1进行舍入  28-16=12  +=  1<<12  0x00001000

  /* clip */						//剪切超出边界的点，使之回落到边界线上
  if (sample >= MAD_F_ONE)				//# define MAD_F_ONE	  MAD_F(0x10000000)
    sample = MAD_F_ONE - 1;				//如果大于最大值，则=上边界
  else if (sample < -MAD_F_ONE)			
    sample = -MAD_F_ONE;				//如果小于最大值，则=下边界

  /* quantize */					// 量化 取第14-29位   30-32bit？
  return sample >> (MAD_F_FRACBITS + 1 - 16);	// >> 28+1-16=13，前面补0
}

/*
 * This is the output callback function. It is called after each frame of
 * MPEG audio data has been completely decoded. The purpose of this callback
 * is to output (or play) the decoded PCM audio.
 */
//输出回调函数：每帧MPEG音频数据被解码完成后调用。用于输出（或播放）解码后的PCM音频。
//播放：将宏定义putchar()重新定义，使数据写入到音频设备
static
enum mad_flow output(void *data,
		     struct mad_header const *header,
		     struct mad_pcm *pcm)
{
  unsigned int nchannels, nsamples;
  mad_fixed_t const *left_ch, *right_ch;

  /* pcm->samplerate contains the sampling frequency */
  //pcm->samplerate包含采样率

  nchannels = pcm->channels;		//通道数
  nsamples  = pcm->length;			//采样的个数
  left_ch   = pcm->samples[0];		//左声道buf
  right_ch  = pcm->samples[1];		//右声道buf

  while (nsamples--) {
    signed int sample;

    /* output sample(s) in 16-bit signed little-endian PCM */
    //以16位、带符号、小端PCM格式输出采样数据

    sample = scale(*left_ch++);		//左声道：裁减和取舍
    putchar((sample >> 0) & 0xff);		//输出倒数第一个字节
    putchar((sample >> 8) & 0xff);		//输出倒数第二个字节

    if (nchannels == 2) {			//如果是2个通道（立体声）
      sample = scale(*right_ch++);		//右声道：裁减和取舍
      putchar((sample >> 0) & 0xff);	//输出倒数第一个字节
      putchar((sample >> 8) & 0xff);	//输出倒数第二个字节
    }
  }

  return MAD_FLOW_CONTINUE;
}


/*
 * This is the error callback function. It is called whenever a decoding
 * error occurs. The error is indicated by stream->error; the list of
 * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
 * header file.
 */
//错误回调函数：解码出错时被调用。
//错误类型在stream->error中有说明。MAD_ERROR_*错误列表在头文件mad.h或stream.h可以找到。
static
enum mad_flow error(void *data,
		    struct mad_stream *stream,
		    struct mad_frame *frame)
{
  struct buffer *buffer = data;

  fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
	  stream->error, mad_stream_errorstr(stream),
	  stream->this_frame - buffer->start);

  /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */
  //可以用return MAD_FLOW_BREAK停止解码

  return MAD_FLOW_CONTINUE;
}


/*
 * This is the function called by main() above to perform all the decoding.
 * It instantiates a decoder object and configures it with the input,
 * output, and error callback functions above. A single call to
 * mad_decoder_run() continues until a callback function returns
 * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
 * signal an error).
 */
//decode()在主函数main()中调用来完成所有的解码。
//它表明了一个解码器，并用input、output、error回调函数进行配置。
//调用mad_decoder_run()，直到回调函数返回MAD_FLOW_STOP来停止解码，或者返回MAD_FLOW_BREAK停止解码并发出一个错误信息。
static
int decode(unsigned char const *start, unsigned long length)
{
  struct buffer buffer;
  struct mad_decoder decoder;
  int result;

  /* initialize our private message structure */
  //初始化私有的信息结构体

  buffer.start  = start;
  buffer.length = length;

  /* configure input, output, and error functions */
  //配置输入、输出、错误函数
  mad_decoder_init(&decoder, &buffer,
		   input, 0 /* header */, 0 /* filter */, output,
		   error, 0 /* message */);

  /* start decoding */
  //开始解码，直到结束或出错
  result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

  /* release the decoder */
  //释放解码器
  mad_decoder_finish(&decoder);

  return result;
}
